package com.zhkc.iess.online.test01.task;

import com.google.common.collect.Lists;
import com.zhkc.iess.online.test01.core.HJ212Data;
import com.zhkc.iess.online.test01.service.ExecutionContext;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;

import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
public class ExecutionContextImpl implements ExecutionContext {

    private final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
    private ExecutionContextContainer executionContextContainer;
    private final Channel channel;


    private final HJ212Data request;
    List<ScheduledFuture<?>> futures = Lists.newArrayList();
    private final Lock lock = new ReentrantLock();
    HJ212Data data;
    private AtomicBoolean notified;
    private final Condition doneCondition;

    public String getQN() {
        return request.getQN();
    }

    long timeout = 10000;

    public ExecutionContextImpl(HJ212Data request, Channel channel, ScheduledThreadPoolExecutor scheduledThreadPoolExecutor, ExecutionContextContainer executionContextContainer) {
        this.notified = new AtomicBoolean(false);
        this.request = request;
        this.doneCondition = this.lock.newCondition();

        this.channel = channel;
        this.executionContextContainer = new ExecutionContextContainer();
        this.scheduledThreadPoolExecutor = scheduledThreadPoolExecutor;

    }

    public void complete(HJ212Data result) {
        if (this.notified.compareAndSet(false, true)) {
            this.lock.lock();// 628
            try {
                this.data = result;
                this.doneCondition.signal();// 641
            } finally {
                this.lock.unlock();// 643
            }
            Iterator<ScheduledFuture<?>> iterator = futures.iterator();
            while (iterator.hasNext()) {
                ScheduledFuture<?> next = iterator.next();
                if (!next.isDone()) {
                    next.cancel(false);
                }
                iterator.remove();
            }
        }
    }

    public void schedule(Runnable command,
                         long delay,
                         TimeUnit unit) {
        futures.add(scheduledThreadPoolExecutor.schedule(command
                , delay, unit
        ));
    }

    public void execute() {
        schedule(() -> {
                    //超时处理（如果设备端超时没有应答，移除QN）
                    executionContextContainer.remove(request.getQN());
                    complete(null);
                }
                , timeout, TimeUnit.MILLISECONDS
        );
        channel.writeAndFlush(request);
    }

    boolean isDone() {
        return null != data;
    }

    /**
     * 执行成功
     */
    @Override
    public boolean get() throws TimeoutException {
        if (!this.isDone()) {
            this.lock.lock();
            try {
                this.doneCondition.await(timeout, TimeUnit.MILLISECONDS);// 717
            } catch (InterruptedException var13) {
                Thread.currentThread().interrupt();
                log.warn("Current thread is interrupted.");
            }
        }
        return Optional.ofNullable(data)
                .map(t -> "1".equals(t.getCps().new Parser().parse().getInfos().get("QnRtn")))
                .orElseThrow(() -> new TimeoutException(getQN()));
    }


}